package pl.edu.pw.elka.jtrzasko.proz.warcaby.controller;

import java.util.concurrent.LinkedBlockingQueue;

import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.client.ClientActionClick;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.client.ClientActionStart;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.server.ServerActionActivePlayer;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.server.ServerActionBoard;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.server.ServerActionGameDraw;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.server.ServerActionGameOver;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.actions.server.ServerActionPlayerColor;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.MessageFactory;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.client.ClientMessage;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.client.ClientMessageClick;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.client.ClientMessageStart;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.server.ServerMessageGameDraw;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.server.ServerMessageGameOver;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.server.ServerMessagePlayerColor;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.server.ServerMessageActivePlayer;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.messages.server.ServerMessageBoard;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.model.Model;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.model.Players;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.model.Position;

/**
 * Glowny kontroler gry. Odpowiada za odbierania wiadomosci od klientow,
 * operacje na modelu oraz wysylanie informacji o stanie gry do klientow.
 * 
 */
public class ServerController implements Runnable {

	/**
	 * Model na ktorym kontorler bedzie pracowal
	 */
	private Model model;
	/**
	 * Kolejka do wstawiania i odbierania wiadomosci od klientow
	 */
	private LinkedBlockingQueue<ClientMessage> clientMessagesQueue;
	/**
	 * Serwer odpowiedzialny za wymiane informacji client/server
	 */
	private Server server;
	/**
	 * Ogolny stan gry
	 */
	private GameState gameState;

	public Model getModel() {
		return model;
	}

	public Players getPlayer() {
		return players;
	}

	/**
	 * Informacje o graczach bioracych udzial w grze
	 */
	private Players players;
	/**
	 * Maszyna stanow przetwarzajaca poszczegolne ruchy graczy
	 */
	private MoveStateMachine moveStateMachine;

	/**
	 * Tworzy nowa instancje kontrolera podpinajac serwer wymieniajacy
	 * infromacje z klientami.
	 * 
	 * @param server
	 *            Glowny serwer wymieniajacy informacje miedzy klientami a
	 *            kontrolerem
	 */
	public ServerController(Server server) {
		this.server = server;
		this.gameState = GameState.WAITING;
		this.model = new Model();
		this.clientMessagesQueue = new LinkedBlockingQueue<ClientMessage>();
		this.players = new Players();
		this.moveStateMachine = new MoveStateMachine();

		MessageFactory.registerMessage(
				ClientMessageClick.class.getSimpleName(),
				new ClientActionClick());
		MessageFactory.registerMessage(
				ClientMessageStart.class.getSimpleName(),
				new ClientActionStart());
	}

	public MoveStateMachine getMoveStateMachine() {
		return moveStateMachine;
	}

	public void run() {
		try {
			while (true) {
				ClientMessage m = clientMessagesQueue.take();
				runActionOnModel(m);
				sendChangesToView();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Wykonuje odpowiednie zapytania na modelu w zaleznosci od wiadomosci
	 * odebranej od klienta
	 * 
	 * @param message
	 *            Wiadomosc od klienta
	 */
	public void runActionOnModel(ClientMessage message) {
		MessageFactory.runClientAction(message, this);
	}

	/**
	 * Wysyla informacje o nowym stanie gry do klientow
	 * 
	 */
	private void sendChangesToView() {
		if (model.isGameFinished())
			gameState = GameState.OVER;
		else if (model.isGameDraw(players.getActivePlayerColor()))
			gameState = GameState.DRAW;

		switch (gameState) {
		case WAITING:
			server.sendActionToClients(players.getNewPlayerColor(),
					new ServerMessagePlayerColor(players.getNewPlayerColor()));
			server.sendActionToClients(players.getNewPlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			if (players.isAllPlayerAdded()) {
				server.sendActionToClients(
						players.getActivePlayerColor(),
						new ServerMessageActivePlayer(players
								.getActivePlayerColor()));
				server.sendActionToClients(
						players.getInactivePlayerColor(),
						new ServerMessageActivePlayer(players
								.getActivePlayerColor()));
				gameState = GameState.GAME;
			}
			break;
		case GAME:
			server.sendActionToClients(players.getActivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(players.getInactivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(
					players.getActivePlayerColor(),
					new ServerMessageActivePlayer(players.getActivePlayerColor()));
			server.sendActionToClients(
					players.getInactivePlayerColor(),
					new ServerMessageActivePlayer(players.getActivePlayerColor()));
			break;
		case OVER:
			server.sendActionToClients(players.getActivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(players.getInactivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(players.getActivePlayerColor(),
					new ServerMessageGameOver(players.getActivePlayerColor()));
			server.sendActionToClients(players.getInactivePlayerColor(),
					new ServerMessageGameOver(players.getActivePlayerColor()));
			break;
		case DRAW:
			server.sendActionToClients(players.getActivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(players.getInactivePlayerColor(),
					new ServerMessageBoard(model.getFigures()));
			server.sendActionToClients(players.getActivePlayerColor(),
					new ServerMessageGameDraw());
			server.sendActionToClients(players.getInactivePlayerColor(),
					new ServerMessageGameDraw());
			break;
		}
	}

	/**
	 * Pobiera kolejke, do ktorej wstawiane sa wiadomosci od klientow
	 * 
	 * @return Kolejka wiadomosci od klientow
	 */
	public LinkedBlockingQueue<ClientMessage> getQueue() {
		return clientMessagesQueue;
	}

	public void playerClick(Position p) {
		do {
			moveStateMachine.nextState(model, p, players.getActivePlayerColor());
			if (moveStateMachine.isMoveFinished())
				players.changeActivePlayer();
		} while (!moveStateMachine.isFinalState());
	}

}
